package com.bac.rentmap.ad.flat.rating.update;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jdo.PersistenceManager;

import org.springframework.beans.factory.annotation.Required;

import com.bac.rentmap.ad.flat.FlatAd;
import com.bac.rentmap.ad.flat.jdo.JdoFlatAdDao;
import com.bac.rentmap.ad.flat.rating.BetterFlatAdProvider;
import com.bac.rentmap.ad.flat.rating.FlatAdRatingCalculator;
import com.bac.rentmap.jdo.JdoSupport;

public class NormalizedRatingIncrementEstimatingServiceImpl extends JdoSupport implements
        NormalizedRatingIncrementEstimatingService {
    private List<RatingAwareFlatAdUpdater> ratingAwareFlatAdUpdaters;
    private FlatAdRatingCalculator flatAdRatingCalculator;
    private BetterFlatAdProvider betterFlatAdProvider;
    private JdoFlatAdDao flatAdDao;

    @Override
    public List<NormalizedRatingIncrementEstimation> estimate(String secureId) {
        PersistenceManager persistenceManager = getPersistenceManager();
        FlatAd flatAd;
        try {
            flatAd = flatAdDao.loadBySecureId(secureId, persistenceManager);
        } finally {
            persistenceManager.close();
        }
        List<Long> ratingsToNormalize = new ArrayList<Long>();
        List<RatingAwareFlatAdUpdater> updaters = new ArrayList<RatingAwareFlatAdUpdater>();
        for (RatingAwareFlatAdUpdater ratingAwareFlatAdUpdater : ratingAwareFlatAdUpdaters) {
            RatingEstimation ratingEstimation = ratingAwareFlatAdUpdater
                    .estimateRatingAfterUpdate(secureId);
            long newRating = ratingEstimation.getNewRating();
            long oldRating = ratingEstimation.getOldRating();
            if (newRating != oldRating) {
                updaters.add(ratingAwareFlatAdUpdater);
                ratingsToNormalize.add(newRating);
                ratingsToNormalize.add(oldRating);
            }
        }
        return buildEstimationsFromNonNormalizedData(ratingsToNormalize,
                updaters, flatAd.getCity());
    }

    private List<NormalizedRatingIncrementEstimation> buildEstimationsFromNonNormalizedData(
            List<Long> ratings, List<RatingAwareFlatAdUpdater> updaters, String city) {
        List<Double> normalizedRatings = flatAdRatingCalculator
                .normalizeRatings(ratings);
        List<NormalizedRatingIncrementEstimation> res = new ArrayList<NormalizedRatingIncrementEstimation>();

        Iterator<Double> normalizedRatingIterator = normalizedRatings
                .iterator();
        Iterator<Long> ratingIterator = ratings.iterator();
        for (Iterator<RatingAwareFlatAdUpdater> updaterIterator = updaters
                .iterator(); updaterIterator.hasNext();) {
            Double newNormalizedRating = normalizedRatingIterator.next();
            Double oldNormalizedRating = normalizedRatingIterator.next();
            Long newRating = ratingIterator.next();
            Long oldRating = ratingIterator.next();
            res.add(buildEstimation(newRating, oldRating, newNormalizedRating,
                    oldNormalizedRating, updaterIterator.next().getClass(), city));
        }
        return res;
    }

    private NormalizedRatingIncrementEstimation buildEstimation(long newRating,
            long oldRating, double newNormalizedRating,
            double oldNormalizedRating,
            Class<? extends RatingAwareFlatAdUpdater> updaterClass, String city) {
        NormalizedRatingIncrementEstimation estimation = new NormalizedRatingIncrementEstimation(
                updaterClass);
        int betterThanNewRatingCount = betterFlatAdProvider
                .countFlatAdsWithBetterRating(city, newRating);
        int betterThanOldRatingCount = betterFlatAdProvider
                .countFlatAdsWithBetterRating(city, oldRating);
        estimation.setPositionIncrement(betterThanNewRatingCount
                - betterThanOldRatingCount);
        estimation
                .setRatingIncrement(newNormalizedRating - oldNormalizedRating);
        return estimation;
    }

    @Required
    public void setRatingAwareFlatAdUpdaters(
            List<RatingAwareFlatAdUpdater> ratingAwareFlatAdUpdaters) {
        this.ratingAwareFlatAdUpdaters = ratingAwareFlatAdUpdaters;
    }

    @Required
    public void setFlatAdRatingCalculator(
            FlatAdRatingCalculator flatAdRatingCalculator) {
        this.flatAdRatingCalculator = flatAdRatingCalculator;
    }

    @Required
    public void setBetterFlatAdProvider(
            BetterFlatAdProvider betterFlatAdProvider) {
        this.betterFlatAdProvider = betterFlatAdProvider;
    }

    public void setFlatAdDao(JdoFlatAdDao flatAdDao) {
        this.flatAdDao = flatAdDao;
    }
}
